﻿using Cemit.PolyProto;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using UnityEngine;

namespace Cemit.NetFramework
{
    /// <summary>
    /// 帧同步管理器
    /// </summary>
    public static class FrameSyncManager
    {
        /// <summary>当操作帧更新（66ms，15帧）时执行</summary>
        public static event Action OnFrameUpdated;

        /// <summary>帧同步的帧率</summary>
        public const int FrameSyncRate = 15;
        
        public static int FrameIndex { get; private set; }

        public static float DeltaTime => 1000f / FrameSyncRate / 1000;

        /// <summary>游戏操作的帧率，所有与游戏状态更改相关的代码都需要乘以这个系数</summary>
        public static float GameScale { get; private set; }

        /// <summary>帧同步是否已开始</summary>
        public static bool IsInitialized { get; private set; }

        /// <summary>判断当前是否为空帧</summary>
        public static bool IsNullFrame => nullFrameCount % nullFrameInterval != 0;


        private static MonoHandler MonoHandle;

        private static int nullFrameInterval;
        private static int nullFrameCount;

        private static FrameSyncRoom gameRoom;
        private static Action frameUpdateAction;
        private static Action tempFrameUpdateAction;
        private static Action nextFrameUpdateAction;
        private static bool isframeUpdating = false;

        //key: playerId:prefix  value:operation
        private static Dictionary<string, OperationListener> operationListeners = new Dictionary<string, OperationListener>();

        private static Dictionary<string, Action> stateListners = new Dictionary<string, Action>();
        private static PlayerState[] playerStateList;

        public static void DelayRun(float time, Action action)
        {
            new FrameDelayTimer(time, action).Start();
        }

        [System.Obsolete("使用gameRoom字段")]
        private static PolyGameRoom polyGameRoom;

        /// <summary>
        /// 初始化 <see cref="FrameSyncManager"/>,
        /// 创建一个 <see cref="MonoHandler"/> 对象并每在每个操作帧读取 <see cref="OperationFrameBuffer"/>
        /// 的帧数据
        /// </summary>
        public static void Initialize(FrameSyncRoom gameRoom, IEnumerable<string> playerIds, int gameFrameRate)
        {
            if (gameFrameRate % FrameSyncRate != 0)
            {
                Debug.LogError("frameRate参数必须设置为15的倍数");
                return;
            }

            GameScale = FrameSyncRate / (float)gameFrameRate;
            nullFrameInterval = gameFrameRate / FrameSyncRate;

            MonoHandle = MonoHandler.Create("OperationFrameMasterMono");
            MonoHandle.InvokeAtNextFrame(() => MonoHandle.OnFixedUpdate += OnFixedUpdate);

            FrameSyncManager.gameRoom = gameRoom;

            // 初始化玩家状态列表
            var playerStateListTemp = new List<PlayerState>();
            foreach (var playerId in playerIds)
            {
                playerStateListTemp.Add(new PlayerState(playerId));
            }
            playerStateList = playerStateListTemp.ToArray();

            // 添加玩家状态变化的监听
            for (int i = 0; i < playerStateList.Length; i++)
            {
                var playerState = playerStateList[i];

                AddOperationListener(OperationPrefix.StateChange, playerState.id, state =>
                {
                    playerState.state = state;

                    Debug.Log($"state {playerState.id}-{playerState.state}");

                    foreach (var item in playerStateList)
                    {
                        if (item.state != state)
                        {
                            return;
                        }
                    }

                    if (stateListners.ContainsKey(state))
                    {
                        stateListners[state]?.Invoke();
                        Debug.Log($"state invoke!");
                    }
                });
            }
            
            IsInitialized = true;
        }

        /// <summary>
        /// 初始化 <see cref="FrameSyncManager"/>,
        /// 创建一个 <see cref="MonoHandler"/> 对象并每在每个操作帧读取 <see cref="OperationFrameBuffer"/>
        /// 的帧数据
        /// </summary>
        [Obsolete("使用Initialize(GameRoomInfo, int)")]
        public static void Initialize(PolyGameRoom gameRoom, int gameFrameRate)
        {
            if (gameFrameRate % FrameSyncRate != 0)
            {
                Debug.LogError("frameRate参数必须设置为15的倍数");
            }

            GameScale = FrameSyncRate / (float)gameFrameRate;
            nullFrameInterval = gameFrameRate / FrameSyncRate;

            MonoHandle = MonoHandler.Create("OperationFrameMasterMono");
            MonoHandle.InvokeAtNextFrame(() => MonoHandle.OnFixedUpdate += OnFixedUpdate);

            polyGameRoom = gameRoom;

            IsInitialized = true;
        }

        private static void OnFixedUpdate()
        {
            if (gameRoom.Remain <= 1)
            {
                nullFrameCount++;

                //当前是空帧
                if (IsNullFrame)
                {
                    return;
                }
            }

            //TODO: 将帧数据存入到字典中方便查询
            IEnumerable<string> frame = gameRoom.PopOperationFrame(out int index);

            if (frame is null)
            {
                return;
            }

            FrameIndex = index;

            foreach (var item in frame)
            {
                //playerId:prefix:operation
                var parts = item.Split(':');

                // 不足三个部分视为无效信息
                if (parts.Length < 3)
                {
                    continue;
                }

                var key = $"{parts[0]}:{parts[1]}";

                // 没有任何地方监听了这个前缀
                if (!operationListeners.ContainsKey(key))
                {
                    Debug.LogWarning("没有监听" + key);
                    continue;
                }

                var operation = parts[2];
                operationListeners[key]?.Invoke(operation);
            }

            isframeUpdating = true;

            frameUpdateAction?.Invoke();

            //添加上当前UpdateAction添加的委托
            frameUpdateAction = tempFrameUpdateAction;
            tempFrameUpdateAction = null;

            //添加上一帧的委托
            frameUpdateAction += nextFrameUpdateAction;
            nextFrameUpdateAction = null;

            OnFrameUpdated?.Invoke();

            isframeUpdating = false;
        }

        public static void InvokeAtFrameUpdate(Action action)
        {
            // 如果正在Update，则加入缓存，等到下次Update执行
            if (isframeUpdating)
            {
                tempFrameUpdateAction += action;
            }
            else
            {
                frameUpdateAction += action;
            }
        }

        public static void InvokeAtNextFrameUpdate(Action action)
        {
            nextFrameUpdateAction += action;
        }

        public static void SendState(string state)
        {
            SendOperation(OperationPrefix.StateChange, state);
        }

        public static void AddStateChangeListener(string state, Action action)
        {
            if (stateListners.ContainsKey(state))
            {
                stateListners[state] += action;
            }
            else
            {
                stateListners.Add(state, action);
            }
        }

        /// <summary>发送一个帧操作, prefix 和 operation 字符串中不可含有字符 ':'</summary>
        public static void SendOperation(string prefix, string operation)
        {
            gameRoom.SendOperation($"{prefix}:{operation}");

            //playerId:prefix:operation
            //gameRoom.SendToServer(new O
            //{
            //    RoomId = gameRoom.ID,
            //    Operation = $"{prefix}:{operation}"
            //});
        }

        public static void AddOperationListener(string prefix, string playerId, OperationListener listener)
        {
            string key = $"{playerId}:{prefix}";
            if (operationListeners.ContainsKey(key))
            {
                operationListeners[key] += listener;
            }
            else
            {
                operationListeners.Add($"{playerId}:{prefix}", listener);
            }
        }

        public delegate void OperationListener(string operation);

        private class PlayerState
        {
            public string id;
            public string state;

            public PlayerState(string playerId)
            {
                id = playerId;
                state = "";
            }
        }
    }
}